#include <string.h>
#include <errno.h>

#include <sys/time.h>
#include <liburing.h>

#include "tcp_log.h"
#include "tcp_conn.h"
#include "tcp_iouring.h"


struct io_uring g_stRing = {};


static int __iouring_HandleCQE(struct io_uring* pstRing, struct io_uring_cqe* pstCQE)
{
    if (pstCQE->res < 0)
    {
        // 发生错误
        TCP_ERROR("An error occurred: %d, %s", pstCQE->res, strerror(-pstCQE->res));
        return -1;
    }

    return TCP_conn_Handle(pstCQE);
}


static void __iouring_EncodeUserData(struct io_uring_sqe* pstSQE, uint32_t uiConnId, TCP_CONN_EVENT_E enEvent)
{
    TCP_USER_DATA_S stUserData = 
    {
        .uiConnId = uiConnId,
        .enEvent  = enEvent,
    };

    io_uring_sqe_set_data64(pstSQE, stUserData.ui64Value);
}

int TCP_iouring_Init(uint32_t uiQLen)
{
    struct io_uring_params stParams = {};

    return io_uring_queue_init_params(uiQLen, &g_stRing, &stParams);
}

void TCP_iouring_Exit()
{
    io_uring_queue_exit(&g_stRing);
}

void TCP_iouring_AddSQE(TCP_CONNECTION_S* pstConn, TCP_CONN_EVENT_E enEvent)
{
    struct io_uring_sqe* pstSQE = io_uring_get_sqe(&g_stRing);

    __iouring_EncodeUserData(pstSQE, pstConn->uiConnId, enEvent);

    switch (enEvent)
    {
        case TCP_CONN_ACCEPT:
        {
            pstConn->ulAddrLen = sizeof(pstConn->stPeerAddr);
            io_uring_prep_accept(pstSQE, pstConn->iFd, (struct sockaddr *)&pstConn->stPeerAddr, 
                                 &pstConn->ulAddrLen , 0);
            break;
        }
        case TCP_CONN_READ:
        {
            io_uring_prep_recv(pstSQE, pstConn->iFd, pstConn->pcBuf, pstConn->ulBufLen, 0);
            break;
        }
        case TCP_CONN_WRITE:
        {
            io_uring_prep_send(pstSQE, pstConn->iFd, pstConn->pcBuf, pstConn->ulDataLen, 0);
            break;
        }
        default:
        {
            break;
        }
    }
}

void TCP_iouring_Schedule(TCP_SERVER_S* pstServer)
{
    struct __kernel_timespec active_ts, idle_ts;
    struct __kernel_timespec* pstWaitTS = &idle_ts;
    struct io_uring_cqe* pstCQE = NULL;
    uint32_t uiWaitNR = 1;
    int iRet;
    struct io_uring* pstRing = &g_stRing;

    idle_ts.tv_sec = 0;
    idle_ts.tv_nsec = 100000000;

    while (pstServer->iRunFlag)
    {
        TCP_DEBUG(pstServer->stConfig.usVerbose, "Submit and wait for %d", uiWaitNR);
        iRet = io_uring_submit_and_wait_timeout(pstRing, &pstCQE, uiWaitNR, pstWaitTS, NULL);
        TCP_DEBUG(pstServer->stConfig.usVerbose, "Submit and wait: %d", iRet);

        if (iRet < 0)
        {
            if (iRet == -ETIME || iRet == -EINTR || iRet == -EAGAIN)
            {
                continue;
            }

            TCP_ERROR("io_uring_submit_and_wait_timeout error, %s", strerror(-iRet));
            break;
        }

        uint32_t uiHead = 0;
        uint32_t uiCount = 0;
        io_uring_for_each_cqe(pstRing, uiHead, pstCQE) 
        {
            __iouring_HandleCQE(pstRing, pstCQE);
            uiCount++;
		}

        io_uring_cq_advance(pstRing, uiCount);
    }
}